<template>
  <!-- :class="{'is-invalid': inputRef.error}": 动态class，如果inputRef.error为true就加上这个类名  -->
  <div class="validate-input-container pb-3 position-relative">
    <!-- v-bind="$attrs": 实现属性的双向绑定 -->
    <input
      v-if="tag !== 'textarea'"
      class="form-control"
      :class="{'is-invalid': inputRef.error}"
      @blur="validateInput"
      v-model="inputRef.val"
      v-bind="$attrs"
    >
    <textarea
      v-else
      class="form-control"
      :class="{'is-invalid': inputRef.error}"
      @blur="validateInput"
      v-model="inputRef.val"
      v-bind="$attrs"
    >
    </textarea>
    <span v-if="inputRef.error" class="invalid-feedback position-absolute mt-1">{{inputRef.message}}</span>
  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, PropType, onMounted, inject, watch, computed } from 'vue'
import { emitter } from './ValidateForm.vue'

// 邮箱正则表达式
const emailReg = /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/
// 校验规则
interface RuleProp {
  type: 'required' | 'email' | 'range' | 'custom';
  message?: string;
  validator?: () => boolean; // custom: 自定义，validator：自定义函数
  min?: { length: number, message: string };
  max?: { length: number, message: string; };
}
export type RulesProp = RuleProp[]
// 支持的标签类型
export type tagType = 'input' | 'textarea'
export default defineComponent({
  name: 'ValidateInput',
  props: {
    rules: Array as PropType<RulesProp>,
    // 自定义组件，双向绑定，需要固定名称定义值
    modelValue: String,
    tag: {
      type: String as PropType<tagType>,
      default: 'input'
    }
  },
  inheritAttrs: false, // 禁用Attribute(属性)继承，保证添加的属性不会加到根组件上
  setup (props, context) {
    // 输入框中的值，错误以及提示信息
    const inputRef = reactive({
      // val: props.modelValue || '', // 可能存在初始值
      // 计算属性的高级写法
      val: computed({
        get: () => props.modelValue || '',
        set: val => {
          context.emit('update:modelValue', val)
        }
      }),
      error: false,
      message: ''
    })
    const clearInput = () => {
      inputRef.val = ''
    }
    // 根据规则，校验输入框中的值
    const validateInput = () => {
      if (props.rules) {
        // 所有校验规则通过才返回true
        const allPassed = props.rules.every(rule => {
          let passed = true // 某条规则的通过与否的临时变量，默认是true
          inputRef.message = rule.message || ''

          // 匹配校验规则
          switch (rule.type) {
            case 'required':
              passed = (inputRef.val.trim() !== '')
              break
            case 'email':
              passed = emailReg.test(inputRef.val)
              break
            case 'range': {
              const { min, max } = rule
              if (min && inputRef.val.trim().length < min.length) {
                passed = false
                inputRef.message = min.message
              }
              if (max && inputRef.val.trim().length > max.length) {
                passed = false
                inputRef.message = max.message
              }
            }
              break
            case 'custom':
              // 三元表达式
              passed = rule.validator ? rule.validator() : true
              break
            default:
              break
          }
          return passed
        })
        // 取反
        inputRef.error = !allPassed
        return allPassed
      }
      return true
    }
    // 组件挂载时触发事件
    onMounted(() => {
      emitter.emit('form-item-created', {
        validator: validateInput,
        clearInput,
        formName: inject('formName')
      })
    })
    return {
      inputRef,
      validateInput
    }
  }
})
</script>

<style>
.validate-input-container .error-message {
  bottom: -5px;
}
</style>
